package com.jfee.dds.datasource;

import com.jfee.dds.dbmgr.TenantDBMgr;
import com.jfee.dds.model.Db;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * 定义动态数据源派生类。从基础的DataSource派生，动态性自己实现。
 *
 * @author sf.xiong
 * @version 2019-05-31
 */
@Component
public class DynamicDataSource extends DataSource {

    private static Logger log = LogManager.getLogger(DynamicDataSource.class);

    @Autowired
    private TenantDBMgr tenantDBMgr;

    /**
     * 改写本方法是为了在请求不同工程的数据时去连接不同的数据库。
     */
    @Override
    public synchronized Connection getConnection() {

        String tenantId = DBIdentifier.getTenantId();

        //1、获取数据源
        DataSource dds = DDSHolder.instance().getDDS(tenantId);

        //2、如果数据源不存在则创建
        if (dds == null) {
            try {
                DataSource newDDS = initDDS(tenantId);
                DDSHolder.instance().addDDS(tenantId, newDDS);
            } catch (IllegalArgumentException | IllegalAccessException e) {
                log.error("Init data source fail. tenantId: {}", tenantId);
                return null;
            }
        }

        dds = DDSHolder.instance().getDDS(tenantId);
        try {
            return dds.getConnection();
        } catch (SQLException e) {
            log.error("Get jdbc connection error. {}", e.getMessage());
            return null;
        }
    }

    /**
     * 以当前数据对象作为模板复制一份。
     *
     * @return dds
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
    private DataSource initDDS(String tenantId) throws IllegalArgumentException, IllegalAccessException {

        DataSource dds = new DataSource();

        // 2、复制PoolConfiguration的属性
        PoolProperties property = new PoolProperties();
        Field[] pFields = PoolProperties.class.getDeclaredFields();
        for (Field f : pFields) {
            f.setAccessible(true);
            Object value = f.get(this.getPoolProperties());

            try {
                f.set(property, value);
            } catch (Exception e) {
                //有一些static final的属性不能修改。忽略。
                log.info("Set value ignored. attr name:" + f.getName());
            }
        }
        dds.setPoolProperties(property);

        // 3、设置数据库名称和IP、端口和用户名、密码
        String urlFormat = this.getUrl();
        Db db = tenantDBMgr.getDataProperties(tenantId);
        String url = String.format(urlFormat, db.getIpAddr(), db.getPort(), db.getDbName());
        dds.setUrl(url);
        dds.setUsername(db.getUserName());
        dds.setPassword(db.getUserPass());

        return dds;
    }
}
